热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

发生|可能会_JMM底层之happnesbefore原则

篇首语:本文由编程笔记#小编为大家整理,主要介绍了JMM底层之happnes-before原则相关的知识,希望对你有一定的参考价值。

篇首语:本文由编程笔记#小编为大家整理,主要介绍了JMM底层之happnes-before原则相关的知识,希望对你有一定的参考价值。



前言

JAVA内存模型中的有序性如果只靠volatile和synchronized,那么并发编程会非常麻烦,但是实际上写代码的时候却并没有这么复杂。原因在于Java语言中有一个happens-before原则。这个原则很重要,是判断数据是否存在竞争、线程是否安全的主要依据。


JVM内存模型

   happen-before是JMM最核心的概念,在了解happen-before原则之前,需要先了解java的内存模型。

*[图片来源于网络]
   java内存模型线程之间主要通过读-写共享变量来完成隐式通信。java中的共享变量是存储在主内存中的,本线程操作的是共享的变量副本,其工作方式是将共享内存中的变量拿出来放在工作内存,操作完成后,再将最新的变量放回共享变量,这时其他的线程就可以获取到最新的共享变量。
    假如线程C和线程B同时操作同一个变量,在理论上来讲,这个时候可能会出现脏读的情况出现。为了避免脏读的这种机制,JMM通过同步机制(控制不同线程间操作发生的先后顺序)来解决,从而对每个线程来讲都是可见的。


happnes-before原则

Java内存模型在JDK1.5版本中引入了Happens-Before原则。
我们编写的程序都要经过优化后(编译器和处理器会对我们的程序进行优化以提高运行效率)才会被运行,优化分为很多种,其中有一种优化叫做重排序,重排序需要遵守happens-before规则


代码重排

执行的代码,在编译和执行的时候,可能不是按照我们写的顺序来执行的。为了提高性能,编译器和处理器都会对代码进行重排序(这就是我们经常口头上说的环境导致的异常问题)。

一般情况下,我们会把重排分为三种:


  • 编译器优化重排
  • 指令级并行优化重排
  • 系统优化重排

什么是happnes-before

其实就是Java内存模型中定义的两项操作之间的排序关系,如果操作A happens-before操作B,就是在说发生在B之前的操作A产生的影响能被B观察到。
下面的例子简单的说明如果存在先行关系,就不用担心指令重排对两个线程的影响,不存在先行关系就要特别小心了:



以下操作在线程A中执行
i=1;
以下操作在线程B中执行
j=i;
以下操作在线程C中执行
i=2;


假设线程A中的操作“i=1” happnes-before 线程B的操作"j=i”", 那我们就可以确定在线程B的操作执行后,变量j的值一定是等于1,得出这个结论的依据有两个:



  • 1 是根据happnes-before原则,“i=1” 的结果可以被观察到;
  • 2 是线程C还没登场,线程A操作结束之后没有其他线程会修改变量i的值。

现在再来考虑线程C,我们依然保持线程A和B之间的先行发生关系,而C出现在线程A和B的操作之间,但是C与B没有先行发生关系,那j的值会是多少呢?



答案是不确定!
1和2都有可能,因为线程C对变量i的影响可能会被线程B观察到,也可能不会,这时候线程B就存在读取到过期数据的风险,不具备多线程安全性。



happnes-before 具体规则


1.程序顺序规则:

在一个线程内,按照控制流顺序,书写在前面的操作先行发生于书写在后面的操作。注意,这里说的是控制流顺序而不是程序代码顺序,因为要考虑分支、循环等结构。

程序顺序规则中所说的每个操作happens-before于该线程中的任意后续操作并不是说前一个操作必须要在后一个操作之前执行,而是指前一个操作的执行结果必须对后一个操作可见,如果不满足这个要求那就不允许这两个操作进行重排序


2.锁定规则:

一个unlock操作happens-before后面对同一个锁的lock操作。
这里必须强调的是“同一个锁",而“后面”是指时间上的先后。
下面的代码,在进入synchronized代码块之前,会自动加锁,在代码块执行完毕后,会自动释放锁。


3.volatile变量规则:

对一个volatile变量的写操作Happens-Before后面对这个变量的读操作。“后面”是指时间上的先后。


4.线程启动规则:

Thread对象的start()方法Happens-Before 此线程的每一个动作


5.线程终止规则:

线程中所有操作都Happens-Before对此线程的终止检测。


6.线程中断规则:

对线程的interrupt方法的调用Happens-Before被中断线程的代码检测到中断事件的发生。


7.对象终结规则:

一个对象的初始化完成,Happens-Before它的finalize方法的开始
线程A等待线程B完成(在线程A中调用线程B的join()方法实现),当线程B完成后(线程A调用线程B的join()方法返回),则线程A能够访问到线程B对共享变量的操作。


8.传递规则:

如果A Happens-Before B,并且B Happens-Before C,则A Happens-Before C。


时间先后和先行的区别,

比如有个共享变量x = 0,A线程在时间上先set其为1,线程B获取这个值,这个时候B获取的是1吗?答案是否定的,应该是不知道。因为这个操作没有任何先行规则匹配,虽然set操作先执行,但是不能确保get操作能获得修改后的值。修改方法很简单,加上synchronized或者定义成volatile。



时间上先发生,不一定是先行发生,那么先行发生,一定在时间上是先发生的吗?不一定,因为指令重排。比如int i = 1; j = 2。指令重排后j可能先被执行,但是根据程序次序规则,在一个线程内i = 1是先行于j=2的。



总结

时间上的先后顺序与Happens-Before原则之间基本没有太大的关系,所以我们衡量并发安全问题的时候不要受到时间顺序的干扰,一切必须以Happens-Before原则为准。


推荐阅读
  • 深入理解Java虚拟机的并发编程与性能优化
    本文主要介绍了Java内存模型与线程的相关概念,探讨了并发编程在服务端应用中的重要性。同时,介绍了Java语言和虚拟机提供的工具,帮助开发人员处理并发方面的问题,提高程序的并发能力和性能优化。文章指出,充分利用计算机处理器的能力和协调线程之间的并发操作是提高服务端程序性能的关键。 ... [详细]
  • Android中高级面试必知必会,积累总结
    本文介绍了Android中高级面试的必知必会内容,并总结了相关经验。文章指出,如今的Android市场对开发人员的要求更高,需要更专业的人才。同时,文章还给出了针对Android岗位的职责和要求,并提供了简历突出的建议。 ... [详细]
  • 本文介绍了Java高并发程序设计中线程安全的概念与synchronized关键字的使用。通过一个计数器的例子,演示了多线程同时对变量进行累加操作时可能出现的问题。最终值会小于预期的原因是因为两个线程同时对变量进行写入时,其中一个线程的结果会覆盖另一个线程的结果。为了解决这个问题,可以使用synchronized关键字来保证线程安全。 ... [详细]
  • Java String与StringBuffer的区别及其应用场景
    本文主要介绍了Java中String和StringBuffer的区别,String是不可变的,而StringBuffer是可变的。StringBuffer在进行字符串处理时不生成新的对象,内存使用上要优于String类。因此,在需要频繁对字符串进行修改的情况下,使用StringBuffer更加适合。同时,文章还介绍了String和StringBuffer的应用场景。 ... [详细]
  • Java和JavaScript是什么关系?java跟javaScript都是编程语言,只是java跟javaScript没有什么太大关系,一个是脚本语言(前端语言),一个是面向对象 ... [详细]
  • HashMap的相关问题及其底层数据结构和操作流程
    本文介绍了关于HashMap的相关问题,包括其底层数据结构、JDK1.7和JDK1.8的差异、红黑树的使用、扩容和树化的条件、退化为链表的情况、索引的计算方法、hashcode和hash()方法的作用、数组容量的选择、Put方法的流程以及并发问题下的操作。文章还提到了扩容死链和数据错乱的问题,并探讨了key的设计要求。对于对Java面试中的HashMap问题感兴趣的读者,本文将为您提供一些有用的技术和经验。 ... [详细]
  • ejava,刘聪dejava
    本文目录一览:1、什么是Java?2、java ... [详细]
  • java drools5_Java Drools5.1 规则流基础【示例】(中)
    五、规则文件及规则流EduInfoRule.drl:packagemyrules;importsample.Employ;ruleBachelorruleflow-group ... [详细]
  • linux进阶50——无锁CAS
    1.概念比较并交换(compareandswap,CAS),是原⼦操作的⼀种,可⽤于在多线程编程中实现不被打断的数据交换操作࿰ ... [详细]
  • 如何用UE4制作2D游戏文档——计算篇
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了如何用UE4制作2D游戏文档——计算篇相关的知识,希望对你有一定的参考价值。 ... [详细]
  • 本文介绍了RPC框架Thrift的安装环境变量配置与第一个实例,讲解了RPC的概念以及如何解决跨语言、c++客户端、web服务端、远程调用等需求。Thrift开发方便上手快,性能和稳定性也不错,适合初学者学习和使用。 ... [详细]
  • 本文介绍了如何使用python从列表中删除所有的零,并将结果以列表形式输出,同时提供了示例格式。 ... [详细]
  • 本文讨论了clone的fork与pthread_create创建线程的不同之处。进程是一个指令执行流及其执行环境,其执行环境是一个系统资源的集合。在调用系统调用fork创建一个进程时,子进程只是完全复制父进程的资源,这样得到的子进程独立于父进程,具有良好的并发性。但是二者之间的通讯需要通过专门的通讯机制,另外通过fork创建子进程系统开销很大。因此,在某些情况下,使用clone或pthread_create创建线程可能更加高效。 ... [详细]
  • OO第一单元自白:简单多项式导函数的设计与bug分析
    本文介绍了作者在学习OO的第一次作业中所遇到的问题及其解决方案。作者通过建立Multinomial和Monomial两个类来实现多项式和单项式,并通过append方法将单项式组合为多项式,并在此过程中合并同类项。作者还介绍了单项式和多项式的求导方法,并解释了如何利用正则表达式提取各个单项式并进行求导。同时,作者还对自己在输入合法性判断上的不足进行了bug分析,指出了自己在处理指数情况时出现的问题,并总结了被hack的原因。 ... [详细]
  • 本文介绍了操作系统的定义和功能,包括操作系统的本质、用户界面以及系统调用的分类。同时还介绍了进程和线程的区别,包括进程和线程的定义和作用。 ... [详细]
author-avatar
允思顾我在
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有